package com.pkk.cloud.support.oauth2.center.runner.configuration;

import com.pkk.cloud.support.oauth2.center.runner.service.IntegrationUserDetailsService;
import com.pkk.cloud.support.oauth2.client.detail.config.DatabaseCachableClientDetailsServiceImpl;
import com.pkk.cloud.support.oauth2.config.integration.IntegrationAuthenticationFilter;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.util.Assert;

/**
 * @description: 授权服务配置器【请求优先由@EnableAuthorizationServer、@EnableResourceServer 处理，剩下的无法匹配的由SpringSecurity处理】
 * @desc1:[]
 * @author: peikunkun
 * @create: 2020-03-17 17:00
 **/
@Configuration
//此注解优先于普通的路径之前,接管/oauth/tokan请求路径【使用此注解启用OAuth2.0授权服务机】
@EnableAuthorizationServer
public class OAuth2ServerConfigurer extends AuthorizationServerConfigurerAdapter {


  @Autowired
  private TokenStore tokenStore;
  @Autowired
  private IntegrationUserDetailsService integrationUserDetailsService;
  @Autowired
  private AuthenticationManager authenticationManager;
  @Autowired
  private JwtAccessTokenConverter jwtAccessTokenConverter;
  @Autowired
  private WebResponseExceptionTranslator webResponseExceptionTranslator;
  @Autowired
  private IntegrationAuthenticationFilter integrationAuthenticationFilter;
  @Resource
  private DatabaseCachableClientDetailsServiceImpl databaseCachableClientDetailsServiceImpl;


  /**
   * @Description: 用来配置令牌端点(Token Endpoint)的安全约束.
   * @Param: security
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/17 0017 下午 5:04
   */
  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security
        // 开启/oauth/token_key验证端口认证权限访问
        .tokenKeyAccess("isAuthenticated()")
        //开启/oauth/check_token验证端口无权限访问
        .checkTokenAccess("permitAll()")
        //让/oauth/token支持client_id以及client_secret作登录认证
        .allowFormAuthenticationForClients()
        //定义身份验证拦截器
        .addTokenEndpointAuthenticationFilter(integrationAuthenticationFilter);
    ;
  }

  /**
   * @Description: 用于定义客户详细信息服务的配置器。客户端详情信息进行初始化，能够把客户端详情信息写在内存中或者是通过数据库来存储调取详情信息
   * @Param: clients
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/17 0017 下午 5:05
   */
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    //自己实现数据库和缓存的操作，实现ClientDetailsService接口
    clients.withClientDetails(databaseCachableClientDetailsServiceImpl);
  }

  /**
   * @Description: 用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
   * <p>
   * JwtAccessTokenConverter是用来生成token的转换器，而token令牌默认是有签名的，且资源服务器需要验证这个签名。此处的加密及验签包括两种方式： 对称加密、非对称加密（公钥密钥）
   * 对称加密需要授权服务器和资源服务器存储同一key值，而非对称加密可使用密钥加密，暴露公钥给资源服务器验签
   * @Param: endpoints
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/17 0017 下午 5:05
   */
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    Assert.notNull(tokenStore, "tokenStore must be set");
    //token的储存规则
    endpoints.tokenStore(tokenStore)
        //设置refreshTokens是否重复使用【所谓重复使用指的是登陆后初次生成的refresh token一直保持不变，直到过期；
        //非重复使用指的是在每一次使用refresh token刷新access token的过程中，refresh token也随之更新，即生成新的refresh token】
        .reuseRefreshTokens(false)
        //访问token的转换器
        .accessTokenConverter(jwtAccessTokenConverter)
        //异常响应
        .exceptionTranslator(webResponseExceptionTranslator)
        //身份验证管理器如认证【认证client_id和client_secret】或认证username和password
        .authenticationManager(authenticationManager)
        //用户详细信息// 没有它，在使用refresh_token的时候会报错 IllegalStateException, UserDetailsService is required.
        .userDetailsService(integrationUserDetailsService);
  }


}
